-module(gs).

-export([ps/0]).
-export([ss/0]).
-export([gL/4]).
-export([serverLoop/2]).




% spawn process a process listener to receive messages
%pass to setup the players name, the location of the server, the current location (0) and id of the listener
ps() -> Pid =spawn(fun listener/0),{ok,[Server]} =  io:fread("Please enter ip of server>  ","~a"), {ok,[Name]} =  io:fread("Welcome please enter your name>  ","~s"), io:fwrite("Welcome to Marist College, you may enter talk at anytime to communicate with other players or type quit to quit, enter new to begin a new game"), setup(Name,Server,0,Pid) .

%spawn a process to listen for moves, register player names with their listener, tell user that server has started
ss() -> Pid =spawn (gs,serverLoop,[[],[]]), register(id, Pid), "server started".

%listen for messages and display them upon receival
listener() ->

 receive
 {Player,Say} -> io:fwrite("~s says ~p ~n",[Player,Say]), listener()
end.

%if the id is not already on idlist, register the id to the player name and add the id to id list
addIfNeed(IdList,_,_,NotNeeded) when NotNeeded -> IdList ;
addIfNeed(IdList,Id,Name,_) -> put(list_to_atom(Name),Id), [Id | IdList].

%keep track of LocList and IdList
serverLoop(LocList,IdList) ->
%if the player wants to talk to all send it to each id in IdList
receive
{'talk',Player,Say,"all"} -> io:fwrite("~n ~s said ~p",[Player,Say]), lists:foreach(fun(X) -> X! {Player,Say} end, IdList),serverLoop(LocList,IdList);

%if the player wants to talk to one player only send message to that player
{'talk',Player,Say,Receiver} -> IDR=get(list_to_atom(Receiver)),  IDR ! {Player,Say}, serverLoop(LocList,IdList);

%if message is change of location for a player, update LocList and IdList if necesary
  {ID, Loc, Name,Pid} -> 
  UpdatedIdList=addIfNeed(IdList,Pid,Name,lists:member(Pid,IdList)),

RemovedLocList=lists:filter(fun(X) -> nameIs(Name,X) end ,LocList),
UpdatedLocList=lists:append([{Name,Loc}],RemovedLocList),
io:fwrite(lists:append([Name," moved to ",locName(Loc)] )),io:fwrite(fullcensus(UpdatedLocList,1,[])),ID ! {LocList,Name}, serverLoop(UpdatedLocList,UpdatedIdList)
end.

%if tuple has the same name as the player return false so the player is not notified of his own location
nameIs(Name,{Name,_}) -> false;
nameIs(_,_) -> true.

%if the player is at specified location, add it to STR
playersAt(Loc,{Name,Loc},STR)  ->Temp=Name++", ", [Temp| STR];
playersAt(_,_,STR) -> STR.

%display the people at all locations
fullcensus(_,9,Str)-> Str;
fullcensus(LocList,Loc,Str)->This=lists:map(fun(X) -> playersAt(Loc,X,[]) end, LocList), Temp=lists:append([Str,"~n People at ",locName(Loc),", ",This]), fullcensus(LocList,Loc+1,Temp).

%display players other than Name that are at location Loc
printPlayers(Loc,LocList,Name) -> RemovedLocList=lists:filter(fun(X) -> nameIs(Name,X) end ,LocList), People =lists:map(fun(X) -> playersAt(Loc,X,[]) end, RemovedLocList), 

%if there is at least one person, say along with followed by the other players
writeIfExists(People,lists:flatlength(People)).

writeIfExists(_,0)  ->[];
writeIfExists(People,_) -> io:fwrite("along with "), io:fwrite(People).

%using the information provided, get the text for the next location, along with what the next location is
setup(Name,Server,CurrLoc,Pid) ->  {ok,[Request]} =  io:fread("> ","~a"), io_lib:nl(), {Text,Next}=remote(Server,CurrLoc,Request,Name,Pid),
io:fwrite(Text), setup(Name,Server,Next,Pid).



%call gL to determine next location and the display text for that location
remote(Server,CurrLoc,Direction,Name,Pid) -> rpc:call(Server,gs,gL,[CurrLoc,Direction,Name,Pid]).







%if player entered talk, run subroutine
gL(CurrLoc,'talk',Name,Pid) -> subroutine('talk',Name), id ! {self(),CurrLoc,Name,Pid} ,  peopleAt(CurrLoc),{ textFor(CurrLoc),CurrLoc};

%otherwise tell server new location and tell player new location and text for that location
gL(CurrLoc,Next,Name,Pid) -> 

Loc=nextLoc(CurrLoc,Next) , id ! {self(),Loc,Name,Pid} ,  peopleAt(Loc),{ textFor(Loc),Loc}.


%receive LocList and Name from server and make list of who else is there
peopleAt(Loc) -> 
receive
  {LocList,Name} ->io:fwrite(lists:append(["You are at ",locName(Loc),", "])), printPlayers(Loc,LocList,Name),[]
end.



% message subroutine
subroutine('talk',Player) ->{ok, [Receiver]} =  io:fread("Who would you like to message? (you can say all >)","~s"),

% remove \n from the end
Say =io:get_line("What would you like to say? "),
FormattedSay=lists:delete(10,Say),

%tell setver that Player wants to say a message to Reveiver
 id !{'talk',Player,FormattedSay,Receiver}.

nextLoc(_,'quit')    -> 42;
nextLoc(Loc,'talk') -> Loc;
nextLoc(0,_)         -> 1 ;
nextLoc(1, 'south')   -> 2 ;
nextLoc(1,'west')     -> 3 ;
nextLoc(1,'north')   -> 8 ;
nextLoc(2,'north')     -> 1 ;
nextLoc(3,'east')   -> 1 ;
nextLoc(3,'north')  -> 4 ;
nextLoc(4,'south')  -> 3 ;
nextLoc(4,'east')    -> 8;
nextLoc(4,'north')    -> 6;
nextLoc(5,'north')     -> 7 ;
nextLoc(5,'south')    -> 8 ;
nextLoc(5,'west')    -> 6 ;
nextLoc(6,'south')    ->4;
nextLoc(6,'east')      -> 5;
nextLoc(7,'south')   -> 5;
nextLoc(8,'north') -> 5 ;
nextLoc(8,'south')  -> 1;
nextLoc(8,'west') -> 4 ;
nextLoc(Num,_) when (Num < 9) ->Num;
nextLoc(_,_) -> 42.


locName(1) -> "Donnely";
locName(2) -> "McCann";
locName(3) -> "Champagnat";
locName(4) -> "Hancock Center";
locName(5) -> "Lowell Thomas";
locName(6) -> "Gartland";
locName(7) -> "Fontaine";
locName(8) -> "Library";
locName(_) -> "Route 9".

textFor(0)  -> "welcome";
textFor(1)  -> "Would you like to go north, south or west? ";
textFor(2)  -> "Go north";
textFor(3)  -> "Would you like to go east or north";
textFor(4)  -> "Would you like to go north, south or east?";
textFor(5)  -> "Would you like to go north, south or west?";
textFor(6)  -> "Would you like to go north, south or east?";
textFor(7)  -> "Go south";
textFor(8)  -> "Would you like to go north, south or west?";
textFor(_) -> "Game Over".
